"""Stub file for the array module.

This is only needed so that the static type checker can find the types for the extension
methods we have written in Rust. The file will never be used by Python at runtime.
"""

from __future__ import annotations

from collections.abc import Callable, Iterable, Iterator, MutableSequence
from typing import Any, ClassVar, Literal, Protocol, SupportsInt, overload

from .types import FSharpRef

ArrayType = Literal[
    "Int8",
    "UInt8",
    "Int16",
    "UInt16",
    "Int32",
    "UInt32",
    "SupportsInt",
    "USupportsInt",
    "Int64",
    "UInt64",
    "Float32",
    "Float64",
    "String",
    "Generic",
]

class FSharpArray[T](MutableSequence[T]):
    # Special methods
    def __init__(self, elements: Iterable[T] | None = None) -> None: ...
    def __bytes__(self) -> bytes: ...
    def __delitem__(self, idx: int | slice) -> None: ...
    @overload
    def __getitem__(self, idx: int) -> T: ...
    @overload
    def __getitem__(self, idx: slice) -> FSharpArray[T]: ...
    def __iter__(self) -> Iterator[T]: ...
    def __len__(self) -> int: ...
    def __setitem__(self, idx: int | slice, value: Any) -> None: ...

    # Static methods
    @staticmethod
    def create(count: SupportsInt, value: T) -> FSharpArray[T]: ...
    @staticmethod
    def initialize(
        count: SupportsInt, initializer: Callable[[SupportsInt], T], cons: Any | None = None
    ) -> FSharpArray[T]: ...

    # Instance methods (alphabetically sorted)
    def add_in_place(self, value: T) -> None: ...
    def add_range_in_place(self, values: Iterable[T]) -> None: ...
    def append(self, array2: FSharpArray[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...  # type: ignore[override]
    def average(self, averager: IGenericAverager[T]) -> T: ...
    def average_by[U](self, projection: Callable[[T], U], averager: IGenericAverager[U]) -> U: ...
    def choose[U](self, chooser: Callable[[T], U | None], cons: FSharpCons[U] | None = None) -> FSharpArray[U]: ...
    def chunk_by_size(self, size: SupportsInt) -> list[FSharpArray[T]]: ...
    def collect[U](self, mapping: Callable[[T], FSharpArray[U]], cons: Any | None = None) -> FSharpArray[U]: ...
    def compare_to(self, comparer: Callable[[T, T], SupportsInt], other: FSharpArray[T]) -> SupportsInt: ...
    def compare_with(self, comparer: Callable[[T, T], SupportsInt], other: FSharpArray[T]) -> SupportsInt: ...
    def contains(self, value: T, eq: IEqualityComparer[T] | None = None) -> bool: ...
    def copy(self) -> FSharpArray[T]: ...
    def copy_to(
        self, source_index: SupportsInt, target: FSharpArray[T], target_index: SupportsInt, count: SupportsInt
    ) -> None: ...
    def equals_with(self, equals: Callable[[T, T], bool], other: FSharpArray[T]) -> bool: ...
    def exists(self, predicate: Callable[[T], bool]) -> bool: ...
    def exists_offset(self, predicate: Callable[[T], bool], index: SupportsInt) -> bool: ...
    def fill(self, target_index: SupportsInt, count: SupportsInt, value: T) -> FSharpArray[T]: ...
    def filter(self, predicate: Callable[[T], bool]) -> FSharpArray[T]: ...
    def find(self, predicate: Callable[[T], bool]) -> T: ...
    def find_back(self, predicate: Callable[[T], bool]) -> T: ...
    def find_index(self, predicate: Callable[[T], bool]) -> SupportsInt: ...
    def find_index_back(self, predicate: Callable[[T], bool]) -> SupportsInt: ...
    def find_last_index(self, predicate: Callable[[T], bool]) -> SupportsInt: ...
    def fold[S](self, folder: Callable[[S, T], S], state: S) -> S: ...
    def fold_back[S](self, folder: Callable[[T, S], S], state: S) -> S: ...
    def fold_back2[T1, T2, S](self, folder: Callable[[T1, T2, S], S], array2: FSharpArray[T2], state: S) -> S: ...
    def fold_back_indexed[S](self, folder: Callable[[SupportsInt, T, S], S], state: S) -> S: ...
    def fold_indexed(
        self, folder: Callable[[int, Any, Any], Any], state: Any
    ) -> Any: ...  # Use Any for fold_indexed function types
    def for_all(self, predicate: Callable[[T], bool]) -> bool: ...
    def get_sub_array(
        self, start_index: SupportsInt, count: SupportsInt, cons: FSharpCons[T] | None = None
    ) -> FSharpArray[T]: ...
    def head(self) -> T: ...
    def index_of(
        self,
        item: T,
        start: SupportsInt | None = None,
        count: SupportsInt | None = None,
        eq: IEqualityComparer[T] | None = None,
    ) -> SupportsInt: ...
    def indexed(self) -> FSharpArray[tuple[SupportsInt, T]]: ...
    def insert(self, index: SupportsInt, value: Any) -> None: ...
    def insert_at(self, index: SupportsInt, value: T, cons: Any | None = None) -> None: ...
    def insert_many_at(self, index: SupportsInt, values: FSharpArray[T], cons: Any | None = None) -> FSharpArray[T]: ...
    def insert_range_in_place(self, index: SupportsInt, values: Iterable[T]) -> None: ...
    def item(self, index: SupportsInt) -> T: ...
    def iterate(self, action: Callable[[Any], None]) -> None: ...
    def iterate_indexed(self, action: Callable[[int, Any], None]) -> None: ...
    def last(self) -> T: ...
    def map[U](self, f: Callable[[T], U], cons: Any | None = None) -> FSharpArray[U]: ...
    def map2[T1, T2, U](
        self, f: Callable[[T1, T2], U], array1: FSharpArray[T1], array2: FSharpArray[T2], cons: Any | None = None
    ) -> FSharpArray[U]: ...
    def map3[T1, T2, T3, U](
        self,
        f: Callable[[T1, T2, T3], U],
        array1: FSharpArray[T1],
        array2: FSharpArray[T2],
        array3: FSharpArray[T3],
        cons: Any | None = None,
    ) -> FSharpArray[U]: ...
    def map_fold[S, R](
        self,
        mapping: Callable[[S, T], tuple[R, S]],
        state: S,
        cons: Any | None = None,
    ) -> tuple[FSharpArray[R], S]: ...
    def map_fold_back[S, R](
        self,
        mapping: Callable[[T, S], tuple[R, S]],
        state: S,
        cons: Any | None = None,
    ) -> tuple[FSharpArray[R], S]: ...
    def map_indexed[U](self, f: Callable[[SupportsInt, T], U], cons: Any | None = None) -> FSharpArray[U]: ...
    def map_indexed2[T1, T2, U](
        self,
        f: Callable[[SupportsInt, T1, T2], U],
        array1: FSharpArray[T1],
        array2: FSharpArray[T2],
        cons: Any | None = None,
    ) -> FSharpArray[U]: ...
    def map_indexed3[T1, T2, T3, U](
        self,
        f: Callable[[int, T1, T2, T3], U],
        array1: FSharpArray[T1],
        array2: FSharpArray[T2],
        array3: FSharpArray[T3],
        cons: Any | None = None,
    ) -> FSharpArray[U]: ...
    def max(self, comparer: IComparer[T]) -> T: ...
    def max_by[U](self, projection: Callable[[T], U], comparer: IComparer[U]) -> T: ...
    def min(self, comparer: IComparer[T]) -> T: ...
    def min_by[U](self, projection: Callable[[T], U], comparer: IComparer[U]) -> T: ...
    def pairwise(self) -> FSharpArray[tuple[T, T]]: ...
    def partition(
        self, f: Callable[[T], bool], cons: FSharpCons[T] | None = None
    ) -> tuple[FSharpArray[T], FSharpArray[T]]: ...
    def permute(self, f: Callable[[SupportsInt], SupportsInt], array: FSharpArray[T]) -> FSharpArray[T]: ...
    def pick[U](self, chooser: Callable[[T], U | None]) -> U: ...
    def reduce(self, folder: Callable[[Any, Any], Any], state: Any) -> Any: ...
    def reduce_back(self, reduction: Callable[[Any, Any], Any], state: Any) -> Any: ...
    def remove_all_in_place(self, predicate: Callable[[T], bool]) -> SupportsInt: ...
    def remove_at(self, index: SupportsInt, cons: Any | None = None) -> FSharpArray[T]: ...
    def remove_in_place(self, item: T) -> bool: ...
    def resize(
        self, new_size: SupportsInt, zero: T | None = None, cons: FSharpCons[T] | None = None
    ) -> FSharpArray[T]: ...
    def reverse(self) -> FSharpArray[T]: ...  # type: ignore # Override returns new array instead of None
    def scan[S](self, folder: Callable[[S, T], S], state: S, cons: Any | None = None) -> FSharpArray[S]: ...
    def scan_back[S](self, folder: Callable[[T, S], S], state: S, cons: Any | None = None) -> FSharpArray[S]: ...
    def set_slice(self, target: FSharpArray[T], lower: SupportsInt | None, upper: SupportsInt | None) -> None: ...
    def singleton(self, value: T, cons: Any | None = None) -> FSharpArray[T]: ...
    def skip(self, count: SupportsInt, cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
    def skip_while(self, predicate: Callable[[T], bool], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
    def sort(self, comparer: IComparer[T]) -> FSharpArray[T]: ...
    def sort_by(self, projection: Callable[[T], Any], comparer: IComparer[T] | None = None) -> FSharpArray[T]: ...
    def sort_in_place(self, comparer: IComparer[T]) -> None: ...
    def sort_in_place_by[U](self, projection: Callable[[T], U], comparer: IComparer[U]) -> None: ...
    def sort_in_place_with(self, comparer: Callable[[Any, Any], SupportsInt]) -> None: ...
    def sort_with(self, comparer: Callable[[T, T], SupportsInt]) -> FSharpArray[T]: ...
    def split_into(self, chunks: int) -> FSharpArray[FSharpArray[T]]: ...
    def sum(self, adder: IGenericAdder[T]) -> T: ...
    def sum_by[U](self, projection: Callable[[T], U], adder: IGenericAdder[U]) -> U: ...
    def tail(self, cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
    def take(self, count: SupportsInt, cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
    def take_while(self, predicate: Callable[[T], bool], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
    def transpose(self, cons: FSharpCons[FSharpArray[T]] | None = None) -> FSharpArray[FSharpArray[T]]: ...
    def truncate(self, count: SupportsInt) -> FSharpArray[T]: ...
    def try_find(self, predicate: Callable[[T], bool]) -> T | None: ...
    def try_find_back(self, predicate: Callable[[Any], bool]) -> Any: ...
    def try_find_index(self, predicate: Callable[[Any], bool]) -> SupportsInt | None: ...
    def try_find_index_back(self, predicate: Callable[[Any], bool]) -> SupportsInt: ...
    def try_head(self) -> T | None: ...
    def try_item(self, index: SupportsInt) -> T | None: ...
    def try_last(self) -> T | None: ...
    def try_pick[U](self, chooser: Callable[[T], U | None]) -> U | None: ...
    def unzip[U](self: FSharpArray[tuple[T, U]]) -> tuple[FSharpArray[T], FSharpArray[U]]: ...
    def update_at(self, index: SupportsInt, value: T, cons: Any | None = None) -> FSharpArray[T]: ...
    def windowed(self, window_size: SupportsInt) -> FSharpArray[FSharpArray[T]]: ...
    def zip[U](self, array2: FSharpArray[U]) -> FSharpArray[tuple[T, U]]: ...

# Loose functions (alphabetically sorted)
def add_in_place[T](array: FSharpArray[T], value: T) -> None: ...
def add_range_in_place[T](array: FSharpArray[T], values: Iterable[T]) -> None: ...
def allocate_array_from_cons[T](cons: FSharpCons[T] | None, length: SupportsInt) -> FSharpArray[T]: ...
def append[T](array1: FSharpArray[T], array2: FSharpArray[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
def average[T](array: FSharpArray[T], averager: IGenericAverager[T]) -> T: ...
def average_by[T, U](projection: Callable[[T], U], array: FSharpArray[T], averager: IGenericAverager[U]) -> U: ...
def choose[T, U](
    chooser: Callable[[T], U | None], array: FSharpArray[T], cons: FSharpCons[U] | None = None
) -> FSharpArray[U]: ...
def chunk_by_size[T](chunk_size: SupportsInt, array: FSharpArray[T]) -> FSharpArray[FSharpArray[T]]: ...
def collect[T, U](
    mapping: Callable[[T], FSharpArray[U]], array: FSharpArray[T], cons: FSharpCons[U] | None = None
) -> FSharpArray[U]: ...
def compare_to[T](
    comparer: Callable[[T, T], SupportsInt], source1: FSharpArray[T], source2: FSharpArray[T]
) -> SupportsInt: ...
def compare_with[T](
    comparer: Callable[[T, T], SupportsInt], array1: FSharpArray[T], array2: FSharpArray[T]
) -> SupportsInt: ...
def concat[T](arrays: Iterable[FSharpArray[T]], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
def contains[T](value: T, array: FSharpArray[T], eq: IEqualityComparer[T] | None = None) -> bool: ...
def copy[T](array: FSharpArray[T]) -> FSharpArray[T]: ...
def copy_to[T](
    source: FSharpArray[T],
    source_index: SupportsInt,
    target: FSharpArray[T],
    target_index: SupportsInt,
    count: SupportsInt,
) -> None: ...
def create[T](count: SupportsInt, value: T) -> FSharpArray[T]: ...
def empty[T](cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
def equals_with[T](equals_func: Callable[[T, T], bool], array1: FSharpArray[T], array2: FSharpArray[T]) -> bool: ...
def exists[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> bool: ...
def exists_offset[T](predicate: Callable[[T], bool], array: FSharpArray[T], index: int) -> bool: ...
def fill[T](array: FSharpArray[T], target_index: int, count: int, value: T) -> FSharpArray[T]: ...
def filter[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> FSharpArray[T]: ...
def find[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> T: ...
def find_back[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> T: ...
def find_index[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> int: ...
def find_index_back[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> int: ...
def find_last_index[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> int: ...
def fold[T, S](folder: Callable[[S, T], S], state: S, array: FSharpArray[T]) -> S: ...
def fold_back[T, S](folder: Callable[[T, S], S], array: FSharpArray[T], state: S) -> S: ...
def fold_back2[T1, T2, S](
    folder: Callable[[T1, T2, S], S], array1: FSharpArray[T1], array2: FSharpArray[T2], state: S
) -> S: ...
def fold_back_indexed[T, S](folder: Callable[[int, T, S], S], array: FSharpArray[T], state: S) -> S: ...
def fold_back_indexed2[T1, T2, S](folder: Callable[[int, T1, T2, S], S], array2: FSharpArray[T2], state: S) -> S: ...
def fold_indexed[T, S](folder: Callable[[int, Any, Any], Any], state: Any, array: FSharpArray[T]) -> Any: ...
def for_all[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> bool: ...
def get_sub_array[T](
    array: FSharpArray[T], start_index: SupportsInt, count: SupportsInt, cons: FSharpCons[T] | None = None
) -> FSharpArray[T]: ...
def head[T](array: FSharpArray[T]) -> T: ...
def index_of[T](
    array: FSharpArray[T],
    item: T,
    start: SupportsInt | None = None,
    count: SupportsInt | None = None,
    eq: IEqualityComparer[T] | None = None,
) -> SupportsInt: ...
def indexed[T](array: FSharpArray[T]) -> FSharpArray[tuple[SupportsInt, T]]: ...
def initialize[T](
    count: SupportsInt, initializer: Callable[[SupportsInt], T], cons: FSharpCons[T] | None = None
) -> FSharpArray[T]: ...
def insert_at[T](index: SupportsInt, value: T, array: FSharpArray[T], cons: FSharpCons[T] | None = None) -> None: ...
def insert_many_at[T](
    index: SupportsInt,
    values: FSharpArray[T] | Iterable[T],
    array: FSharpArray[T],
    cons: FSharpCons[T] | None = None,
) -> FSharpArray[T]: ...
def insert_range_in_place[T](array: FSharpArray[T], index: SupportsInt, values: Iterable[T]) -> None: ...
def item[T](index: SupportsInt, array: FSharpArray[T]) -> T: ...
def iterate[T](action: Callable[[T], None], array: FSharpArray[T]) -> None: ...
def iterate_indexed[T](action: Callable[[SupportsInt, T], None], array: FSharpArray[T]) -> None: ...
def last[T](array: FSharpArray[T]) -> T: ...
def map[T, U](f: Callable[[T], U], array: FSharpArray[T], cons: FSharpCons[U] | None = None) -> FSharpArray[U]: ...
def map2[T1, T2, U](
    f: Callable[[T1, T2], U], array1: FSharpArray[T1], array2: FSharpArray[T2], cons: FSharpCons[U] | None = None
) -> FSharpArray[U]: ...
def map3[T1, T2, T3, U](
    f: Callable[[T1, T2, T3], U],
    array1: FSharpArray[T1],
    array2: FSharpArray[T2],
    array3: FSharpArray[T3],
    cons: FSharpCons[U] | None = None,
) -> FSharpArray[U]: ...
def map_fold[T, S, R](
    mapping: Callable[[S, T], tuple[R, S]], state: S, array: FSharpArray[T], cons: FSharpCons[R] | None = None
) -> tuple[FSharpArray[R], S]: ...
def map_fold_back[T, S, R](
    mapping: Callable[[T, S], tuple[R, S]], array: FSharpArray[T], state: S, cons: FSharpCons[R] | None = None
) -> tuple[FSharpArray[R], S]: ...
def map_indexed[T, U](
    f: Callable[[SupportsInt, T], U], array: FSharpArray[T], cons: FSharpCons[U] | None = None
) -> FSharpArray[U]: ...
def map_indexed2[T1, T2, U](
    f: Callable[[SupportsInt, T1, T2], U],
    array1: FSharpArray[T1],
    array2: FSharpArray[T2],
    cons: FSharpCons[U] | None = None,
) -> FSharpArray[U]: ...
def map_indexed3[T1, T2, T3, U](
    f: Callable[[SupportsInt, T1, T2, T3], U],
    array1: FSharpArray[T1],
    array2: FSharpArray[T2],
    array3: FSharpArray[T3],
    cons: FSharpCons[U] | None = None,
) -> FSharpArray[U]: ...
def max[T](array: FSharpArray[T], comparer: IComparer[T]) -> T: ...
def max_by[T, U](projection: Callable[[T], U], array: FSharpArray[T], comparer: IComparer[U]) -> T: ...
def min[T](array: FSharpArray[T], comparer: IComparer[T]) -> T: ...
def min_by[T, U](projection: Callable[[T], U], array: FSharpArray[T], comparer: IComparer[U]) -> T: ...
def of_seq[T](seq: Iterable[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
def pairwise[T](array: FSharpArray[T]) -> FSharpArray[tuple[T, T]]: ...
def partition[T](
    f: Callable[[T], bool], array: FSharpArray[T], cons: FSharpCons[T] | None = None
) -> tuple[FSharpArray[T], FSharpArray[T]]: ...
def permute[T](f: Callable[[SupportsInt], SupportsInt], array: FSharpArray[T]) -> FSharpArray[T]: ...
def pick[T, U](chooser: Callable[[T], U | None], array: FSharpArray[T]) -> U: ...
def reduce[T](reduction: Callable[[T, T], T], array: FSharpArray[T]) -> T: ...
def reduce_back[T](reduction: Callable[[T, T], T], array: FSharpArray[T]) -> T: ...
def remove_all_in_place[T](array: FSharpArray[T], predicate: Callable[[T], bool]) -> SupportsInt: ...
def remove_at[T](index: SupportsInt, array: FSharpArray[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
def remove_in_place[T](array: FSharpArray[T], item: T) -> bool: ...
def remove_many_at[T](index: SupportsInt, count: SupportsInt, array: FSharpArray[T]) -> FSharpArray[T]: ...
def resize[T](
    array: FSharpRef[FSharpArray[T]], new_size: SupportsInt, zero: T | None = None, cons: FSharpCons[T] | None = None
) -> FSharpArray[T]: ...
def reverse[T](array: FSharpArray[T]) -> FSharpArray[T]: ...
def scan[T, S](
    folder: Callable[[S, T], S], state: S, array: FSharpArray[T], cons: FSharpCons[S] | None = None
) -> FSharpArray[S]: ...
def scan_back[T, S](
    folder: Callable[[T, S], S], array: FSharpArray[T], state: S, cons: FSharpCons[S] | None = None
) -> FSharpArray[S]: ...
def set_slice[T](
    target: FSharpArray[T], lower: SupportsInt | None, upper: SupportsInt | None, array: FSharpArray[T]
) -> None: ...
def singleton[T](value: T, cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
def skip[T](count: SupportsInt, array: FSharpArray[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
def skip_while[T](
    predicate: Callable[[T], bool], array: FSharpArray[T], cons: FSharpCons[T] | None = None
) -> FSharpArray[T]: ...
def sort[T](array: FSharpArray[T], comparer: IComparer[T]) -> FSharpArray[T]: ...
def sort_by[T](
    projection: Callable[[T], Any], array: FSharpArray[T], comparer: IComparer[T] | None = None
) -> FSharpArray[T]: ...
def sort_in_place[T](array: FSharpArray[T], comparer: IComparer[T]) -> None: ...
def sort_in_place_by[T, U](projection: Callable[[T], U], array: FSharpArray[T], comparer: IComparer[U]) -> None: ...
def sort_in_place_with[T](compare_func: Callable[[T, T], SupportsInt], array: FSharpArray[T]) -> None: ...
def sort_with[T](comparer: Callable[[T, T], SupportsInt], array: FSharpArray[T]) -> FSharpArray[T]: ...
def split_into[T](chunks: SupportsInt, array: FSharpArray[T]) -> FSharpArray[FSharpArray[T]]: ...
def sum[T](array: FSharpArray[T], adder: IGenericAdder[T]) -> T: ...
def sum_by[T, U](projection: Callable[[T], U], array: FSharpArray[T], adder: IGenericAdder[U]) -> U: ...
def tail[T](array: FSharpArray[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
def take[T](count: SupportsInt, array: FSharpArray[T], cons: FSharpCons[T] | None = None) -> FSharpArray[T]: ...
def take_while[T](
    predicate: Callable[[T], bool], array: FSharpArray[T], cons: FSharpCons[T] | None = None
) -> FSharpArray[T]: ...
def transpose[T](
    array: FSharpArray[FSharpArray[T]] | Iterable[FSharpArray[T]], cons: FSharpCons[FSharpArray[T]] | None = None
) -> FSharpArray[FSharpArray[T]]: ...
def truncate[T](count: SupportsInt, array: FSharpArray[T]) -> FSharpArray[T]: ...
def try_find[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> T | None: ...
def try_find_back[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> T | None: ...
def try_find_index[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> SupportsInt | None: ...
def try_find_index_back[T](predicate: Callable[[T], bool], array: FSharpArray[T]) -> SupportsInt | None: ...
def try_head[T](array: FSharpArray[T]) -> T | None: ...
def try_item[T](index: SupportsInt, array: FSharpArray[T]) -> T | None: ...
def try_last[T](array: FSharpArray[T]) -> T | None: ...
def try_pick[T, U](chooser: Callable[[T], U | None], array: FSharpArray[T]) -> U | None: ...
def unzip[T, U](array: FSharpArray[tuple[T, U]]) -> tuple[FSharpArray[T], FSharpArray[U]]: ...
def update_at[T](
    index: SupportsInt, value: T, array: FSharpArray[T], cons: FSharpCons[T] | None = None
) -> FSharpArray[T]: ...
def windowed[T](window_size: SupportsInt, array: FSharpArray[T]) -> FSharpArray[FSharpArray[T]]: ...
def zip[T, U](array1: FSharpArray[T], array2: FSharpArray[U]) -> FSharpArray[tuple[T, U]]: ...

class FSharpCons[T]:
    array_type: ClassVar[str]
    def __init__(self, array_type: ArrayType) -> None: ...
    def __call__(self, length: SupportsInt) -> FSharpArray[T]: ...
    def allocate(self, length: SupportsInt) -> FSharpArray[T]: ...

class IGenericAdder[T](Protocol):
    @property
    def GetZero(self) -> Callable[[], T]: ...
    @property
    def Add(self) -> Callable[[T, T], T]: ...

class IGenericAverager[T](Protocol):
    @property
    def GetZero(self) -> Callable[[], T]: ...
    @property
    def Add(self) -> Callable[[T, T], T]: ...
    @property
    def DivideByInt(self) -> Callable[[T, SupportsInt], T]: ...

class IEqualityComparer[T](Protocol):
    def Equals(self, x: T, y: T) -> bool: ...

class IComparer[T](Protocol):
    def Compare(self, x: T, y: T) -> SupportsInt: ...
